package com.topisv.tms.service.lpn;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.topisv.tms.costants.StorageConstants;
import com.topisv.tms.entity.lpn.Box;
import com.topisv.tms.entity.lpn.LPN;
import com.topisv.tms.entity.lpn.Pallet;
import com.topisv.tms.entity.lpn.StorageLink;
import com.topisv.tms.entity.lpn.StorageOrder;
import com.topisv.tms.repository.lpn.PalletDao;
import com.topisv.tms.repository.lpn.StorageOrderDao;
import com.topisv.tms.service.base.BaseService;
import com.topisv.tms.utils.enums.StorageEnum;
import com.topisv.tms.web.lpn.vo.PalletVo;

@Component
@Transactional(readOnly = true)
public class PalletService extends BaseService {
    
    private static Logger LOG = LoggerFactory.getLogger(PalletService.class);
    
    @Autowired
    private PalletDao palletDao;
    
    @Autowired
    private StorageOrderDao storageOrderDao;
    
    @Autowired
    private StorageOrderService storageOrderService;
    
    @Autowired
    private StorageLinkService storageLinkService;
    
    @Autowired
    private BoxService boxService;
    
    @Autowired
    private LpnStorageService lpnStorageService;
    
    /**
     * 检测托盘是否为空闲状态
     * 
     * @param lpn
     * @return [参数说明]
     * 
     * @return boolean [返回类型说明]
     * @exception throws [违例类型] [违例说明]
     * @see [类、类#方法、类#成员]
     */
    public boolean checkPalletFeeByLpn(String lpn) {
        boolean result = false;
        List<Pallet> pallets = palletDao.existsByLpnAndStatus(lpn, StorageConstants.STATUS_FREE);
        if (null != pallets && pallets.size() > 0) {
            if (pallets.size() == 1) {
                Pallet pallet = pallets.get(0);
                if (null != pallet) {
                    result = StorageConstants.STATUS_FREE == pallet.getStatus() ? true : false;
                }
            } else {
                LOG.info("checkPalletIsUseByLpn pallet not only one.");
            }
        } else {
            LOG.info("checkPalletIsUseByLpn pallet not find.");
        }
        return result;
    }
    /**
     * 检测订单是否为绑定状态
     * 
     * @param orderno
     * @return [参数说明]
     * 
     * @return boolean [返回类型说明]
     * @exception throws [违例类型] [违例说明]
     * @see [类、类#方法、类#成员]
     */
    public List<StorageOrder> checkPalletFeeByorderNo(String ordernos_code) {
    	if(StringUtils.isNotBlank(ordernos_code)){
    		String[] orderno = ordernos_code.split(",");
            List<StorageOrder> soList = storageOrderDao.getStorageOrderByOrderAndStatus(Arrays.asList(orderno), StorageConstants.StorageOrder.STATUS_LOADED) ;
            return soList;
    	}
    	return null;
    }
    
    /**
     * 
     * 根据仓库编码获取当前的最大编号
     * 
     * @param warehouseCode
     * @return [参数说明]
     * 
     * @return int [返回类型说明]
     * @exception throws [违例类型] [违例说明]
     * @see [类、类#方法、类#成员]
     */
    public int getMaxPalletNumWhc(String warehouseCode) {
        return palletDao.getMaxPalletNumWhc(warehouseCode);
    }
    
    
    /**
     * 根据ID查询pallet
     * 
     * @param id
     * @return [参数说明]
     * 
     * @return Pallet [返回类型说明]
     * @exception throws [违例类型] [违例说明]
     * @see [类、类#方法、类#成员]
     */
    public Pallet getPalletById(Long id) {
        return palletDao.findOne(id);
    }
    
    /**
     * 根据ID查询pallet,返回palletVo对象
     * 
     * @param id
     * @return [参数说明]
     * 
     * @return PalletVo [返回类型说明]
     * @exception throws [违例类型] [违例说明]
     * @see [类、类#方法、类#成员]
     */
    public PalletVo getPalletVoById(Long id) {
        PalletVo vo = null;
        if (null != id && id > 0) {
            vo = entityToVo(getPalletById(id));
        }
        return vo;
    }
    
    /**
     * 根据LPN查询pallet
     * 
     * @param lpn
     * @return [参数说明]
     * 
     * @return Pallet [返回类型说明]
     * @exception throws [违例类型] [违例说明]
     * @see [类、类#方法、类#成员]
     */
    public Pallet getPalletByLpn(String lpn) {
        if (StringUtils.isNotBlank(lpn)) {
            return palletDao.getPalletByLpn(lpn);
        } else {
            return null;
        }
    }
    
    /**
     * 根据LPN查询pallet,返回palletVo对象
     * 
     * @param lpn
     * @return [参数说明]
     * 
     * @return Pallet [返回类型说明]
     * @exception throws [违例类型] [违例说明]
     * @see [类、类#方法、类#成员]
     */
    public PalletVo getPalletVoByLpn(String lpn) {
        if (StringUtils.isNotBlank(lpn)) {
            return entityToVo(palletDao.getPalletByLpn(lpn));
        } else {
            return null;
        }
    }
    
    /**
     * 分页查询数据
     * 
     * @param palletVo
     * @param page
     * @return [参数说明]
     * 
     * @return Page<Pallet> [返回类型说明]
     * @exception throws [违例类型] [违例说明]
     * @see [类、类#方法、类#成员]
     */
    public Page<Pallet> findAll(PalletVo palletVo, PageRequest page) {
        Specification<Pallet> spec = expWherePallet(null == palletVo ? new PalletVo() : palletVo);
        page = null == page ? new PageRequest(1, 10) : page;
        return palletDao.findAll(spec,
            buildPageRequest(page.getPageNumber(), page.getPageSize(), page.getSort().toString()));
    }
    
    /**
     * 根据参数查询数据。
     * 是否分页，根据palletVo.getPage()来确定
     * 
     * @param palletVo
     * @return [参数说明]
     * 
     * @return List<PalletVo> [返回类型说明]
     * @exception throws [违例类型] [违例说明]
     * @see [类、类#方法、类#成员]
     */
    public List<PalletVo> findAll(PalletVo palletVo) {
        List<PalletVo> voList = null;
        if (null != palletVo) {
            List<Pallet> pallets = null;
            Specification<Pallet> spec = expWherePallet(palletVo);
            if (null == palletVo.getPage()) {
                pallets = palletDao.findAll(spec);
            } else {
                PageRequest page = palletVo.getPage();
                pallets = palletDao
                    .findAll(spec,
                        buildPageRequest(page.getPageNumber(), page.getPageSize(), page.getSort().toString()))
                    .getContent();
            }
            if (null != pallets) {
                voList = new ArrayList<PalletVo>(pallets.size());
                for (Pallet pallet : pallets) {
                    voList.add(entityToVo(pallet));
                }
            }
        }
        return voList;
    }
    
    
    /**
     * 托盘从空闲状态更新为使用状态
     * Status from StorageConstants.STATUS_IN_USE to StorageConstants.STATUS_FEE
     * 
     * @param id
     * @param lpn
     * @return [参数说明]
     * 
     * @return int [返回类型说明]
     * @exception throws [违例类型] [违例说明]
     * @see [类、类#方法、类#成员]
     */
    public int updToUseByIdOrLpn(long id, String lpn) {
        return palletDao.loadedPalletByIdOrLpn(StorageConstants.STATUS_IN_USE, id, lpn, StorageConstants.STATUS_FREE);
    }
    
    /**
     * 托盘从空闲状态更新为使用状态
     * Status from StorageConstants.STATUS_IN_USE to StorageConstants.STATUS_FEE
     * 
     * @param id
     * @param lpn
     * @return [参数说明]
     * 
     * @return int [返回类型说明]
     * @exception throws [违例类型] [违例说明]
     * @see [类、类#方法、类#成员]
     */
    public int updToUseByIdAndLpn(long id, String lpn) {
        return palletDao.loadedPalletByIdAndLpn(StorageConstants.STATUS_IN_USE, id, lpn, StorageConstants.STATUS_FREE);
    }
    
    /**
     * 托盘从空闲状态更新为使用状态
     * Status from StorageConstants.STATUS_IN_USE to StorageConstants.STATUS_FEE
     * 
     * @param id
     * @param lpn
     * @return [参数说明]
     * 
     * @return int [返回类型说明]
     * @exception throws [违例类型] [违例说明]
     * @see [类、类#方法、类#成员]
     */
    public int updToUseById(long id) {
        return palletDao.loadedPalletById(StorageConstants.STATUS_IN_USE, id, StorageConstants.STATUS_FREE);
    }
    
    /**
     * 托盘从空闲状态更新为使用状态
     * Status from StorageConstants.STATUS_IN_USE to StorageConstants.STATUS_FEE
     * 
     * @param lpn
     * @return [参数说明]
     * 
     * @return int [返回类型说明]
     * @exception throws [违例类型] [违例说明]
     * @see [类、类#方法、类#成员]
     */
    public int updToUseByLpn(String lpn) {
        return palletDao.loadedPalletByLpn(StorageConstants.STATUS_IN_USE, lpn, StorageConstants.STATUS_FREE);
    }
    
    /**
     * 修改托盘
     * 
     * @param pallet
     * @return [参数说明]
     * 
     * @return Pallet [返回类型说明]
     * @exception throws [违例类型] [违例说明]
     * @see [类、类#方法、类#成员]
     */
    public Pallet modifyPallet(Pallet pallet) {
        return palletDao.save(pallet);
    }
    
    /**
     * 根据ids批量修改状态
     * 
     * @param toStatus 修改后的状态
     * @param ids [以逗号分隔的ids]
     * 
     * @return void [返回类型说明]
     * @exception throws [违例类型] [违例说明]
     * @see [类、类#方法、类#成员]
     */
    public int modifyStatusByIds(int toStatus, List<Long> ids) {
        return palletDao.modifyStatusByIds(toStatus, ids);
    }
    
    /**
     * 批量新增托盘接口
     * 托盘与箱子与lpn的关联为此内部实现逻辑，调用者不用关心。
     * 
     * @param pallets [参数说明]
     * 
     * @return void [返回类型说明]
     * @exception throws [违例类型] [违例说明]
     * @see [类、类#方法、类#成员]
     */
    @Transactional(propagation = Propagation.REQUIRED, readOnly = true)
    public List<Pallet> savePallets(List<Pallet> pallets) {
        List<Pallet> palletList = null;
        if (null != pallets && pallets.size() > 0) {
            int size = pallets.size();
            Pallet pallet = null;
            Box box = null;
            StorageLink sLink = null;
            // 申请托盘LPN
            List<LPN> palletLpns = lpnStorageService.bindLPN(pallets.get(0).getWarehouseCode(),
                StorageEnum.pallte,
                size);
            // 申请箱子LPN
            List<LPN> boxLpns = lpnStorageService.bindLPN(pallets.get(0).getWarehouseCode(), StorageEnum.box, size);
            if (null != palletLpns && null != boxLpns && palletLpns.size() >= size && boxLpns.size() >= size) {
                palletList = new ArrayList<Pallet>(size);
                List<Box> boxList = new ArrayList<Box>(size);
                List<StorageLink> sLinkList = new ArrayList<StorageLink>(size);
                for (int i = 0; i < size; i++) {
                    pallet = pallets.get(i);
                    // 常规默认属性，通过设置实体的默认值，进行赋值
                    pallet.setCreateTime(new Date());
                    pallet.setLpnNo(palletLpns.get(i).getLpnNo());
                    pallet.setIsFloor(StorageConstants.FLOOR_FALSE);
                    pallet.setStatus(StorageConstants.STATUS_FREE);
                    palletList.add(pallet);
                    
                    box = new Box();
                    box.setIsFloor(StorageConstants.FLOOR_TRUE);
                    box.setIsTop(StorageConstants.TOP_FALSE);
                    box.setStatus(StorageConstants.STATUS_IN_USE);
                    box.setParentLpnNo(pallet.getLpnNo());
                    box.setLpnNo(boxLpns.get(i).getLpnNo());
                    boxList.add(box);
                    
                    sLink = new StorageLink();
                    sLink.setChildLpn(box.getLpnNo());
                    sLink.setParentLpn(pallet.getLpnNo());
                    sLinkList.add(sLink);
                }
                boxService.save(boxList);
                storageLinkService.save(sLinkList);
                palletDao.save(palletList);
            }
        }
        return palletList;
    }
    
    /**
     * 新增托盘接口
     * 
     * @param pallet [参数说明]
     * 
     * @return void [返回类型说明]
     * @exception throws [违例类型] [违例说明]
     * @see [类、类#方法、类#成员]
     */
    @Transactional(propagation = Propagation.REQUIRED, readOnly = true)
    public void savePallets(Pallet pallet) {
        if (null != pallet) {
            Box box = null;
            StorageLink sLink = null;
            // 申请托盘LPN
            List<LPN> palletLpns = lpnStorageService.bindLPN(pallet.getWarehouseCode(), StorageEnum.pallte, 1);
            // 申请箱子LPN
            List<LPN> boxLpns = lpnStorageService.bindLPN(pallet.getWarehouseCode(), StorageEnum.box, 1);
            if (null != palletLpns && null != boxLpns && palletLpns.size() >= 0 && boxLpns.size() >= 0) {
                // 常规默认属性，通过设置实体的默认值，进行赋值
                pallet.setCreateTime(new Date());
                pallet.setLpnNo(palletLpns.get(0).getLpnNo());
                pallet.setStatus(StorageConstants.STATUS_FREE);
                pallet.setIsFloor(StorageConstants.FLOOR_FALSE);
                
                box = new Box();
                box.setName("box_default");
                box.setIsFloor(StorageConstants.FLOOR_TRUE);
                box.setIsTop(StorageConstants.TOP_FALSE);
                box.setStatus(StorageConstants.STATUS_IN_USE);
                box.setParentLpnNo(pallet.getLpnNo());
                box.setLpnNo(boxLpns.get(0).getLpnNo());
                
                sLink = new StorageLink();
                sLink.setChildLpn(box.getLpnNo());
                sLink.setParentLpn(pallet.getLpnNo());
                boxService.save(box);
                storageLinkService.save(sLink);
                palletDao.save(pallet);
            }
        }
    }
    
    /**
     * 根据LpnNo批量卸载托盘的订单(即:托盘与订单解绑)
     * 1、修改托盘状态为：StorageConstants.STATUS_FREE
     * 2、解除托盘与订单关系，改关系状态为：StorageConstants.StorageOrder.STATUS_UNLOAD
     * @return void [返回类型说明]
     * @exception throws [违例类型] [违例说明]
     * @see [类、类#方法、类#成员]
     */
    @Transactional
    public int unloadOrdersByLpnNos(List<String> lpnNos) {
        palletDao.modifyStatusByLpns(StorageConstants.STATUS_FREE, lpnNos);
        return storageOrderService.modifyStatusByLpnNos(StorageConstants.StorageOrder.STATUS_UNLOAD, lpnNos);
    }
    /**
     * 根据LpnNo批量卸载托盘的订单(即:托盘与订单解绑)
     * 1、修改托盘状态为：StorageConstants.STATUS_FREE
     * 2、解除托盘与订单关系，改关系状态为：StorageConstants.StorageOrder.STATUS_UNLOAD
     * add by caomingwu 用于临时处理list无法处理的问题
     * @return void [返回类型说明]
     * @exception throws [违例类型] [违例说明]
     * @see [类、类#方法、类#成员]
     */
    @Transactional
    public int unloadOrdersByLpnNo(String lpnNo) {
        palletDao.modifyStatusByLpn(StorageConstants.STATUS_FREE, lpnNo);
        return storageOrderService.modifyStatusByLpnNos(StorageConstants.StorageOrder.STATUS_UNLOAD, lpnNo);
    }
    /**
     * 根据LpnNo批量修改托盘状态
     * 修改托盘状态
     * @return void [返回类型说明]
     * @exception throws [违例类型] [违例说明]
     * @see [类、类#方法、类#成员]
     */
    @Transactional
    public int modifyStatusByLpns(int status,List<String> lpnNos) {
        return palletDao.modifyStatusByLpns(status, lpnNos);
    }
    /**
     * 根据LpnNo批量修改托盘状态
     * 修改托盘状态
     * add by caomingwu  用于临时解决list无法使用的问题
     * @return void [返回类型说明]
     * @exception throws [违例类型] [违例说明]
     * @see [类、类#方法、类#成员]
     */
    @Transactional
    public int modifyStatusByLpn(int status,String lpnNo) {
        return palletDao.modifyStatusByLpn(status, lpnNo);
    }
    
    /**
     * 根据OrderNo批量卸载托盘的订单(即:托盘与订单解绑)
     * 此方法只提供解除托盘与订单的绑定关系，不会调整托盘的状态。
     * @return void [返回类型说明]
     * @exception throws [违例类型] [违例说明]
     * @see [类、类#方法、类#成员]
     */
    @Transactional
    public int unloadOrdersByOrderNos(List<String> orderNos) {
        return storageOrderService.modifyStatusByOrderNos(StorageConstants.StorageOrder.STATUS_UNLOAD, orderNos);
    }
    
    private PalletVo entityToVo(Pallet pallet) {
        PalletVo vo = null;
        if (null != pallet) {
            vo = new PalletVo();
            vo.setId(pallet.getId());
            vo.setName(pallet.getName());
            vo.setCreateTime(pallet.getCreateTime());
            vo.setCustomerCode(pallet.getCustomerCode());
            vo.setWarehouseCode(pallet.getWarehouseCode());
            vo.setPalletNum(pallet.getPalletNum());
            vo.setLpnNo(pallet.getLpnNo());
            vo.setStatus(pallet.getStatus());
            vo.setRemark(pallet.getRemark());
            // TODO 在使用中的订单数量
        }
        return vo;
    }
    
    /**
     * 导出操作查询条件封装
     * 
     * @param palletVo
     * @return
     */
    private Specification<Pallet> expWherePallet(final PalletVo palletVo) {
        
        return new Specification<Pallet>() {
            @Override
            public Predicate toPredicate(Root<Pallet> r, CriteriaQuery<?> q, CriteriaBuilder cb) {
                Predicate pre = cb.conjunction();
                List<Expression<Boolean>> expression = pre.getExpressions();
                String customerCode = palletVo.getCustomerCode();
                if (StringUtils.isNotBlank(customerCode)) {
                    expression.add(cb.like(r.get("customerCode").as(String.class), customerCode.trim()));
                }
                String warehouseCode = palletVo.getWarehouseCode();
                if (StringUtils.isNotBlank(warehouseCode)) {
                    expression.add(cb.like(r.get("warehouseCode").as(String.class), warehouseCode.trim()));
                }
                String lpnNo = palletVo.getLpnNo() ; 
                if (StringUtils.isNotBlank(lpnNo)) {
                    expression.add(cb.like(r.get("lpnNo").as(String.class), lpnNo.trim()));
                }
                int status = palletVo.getStatus();
                if (status > 0) {
                    expression.add(cb.equal(r.get("status"), status));
                }
                int isValid = palletVo.getIsValid();
                if (isValid >= 0) {
                    expression.add(cb.equal(r.get("isValid"), isValid));
                }
                Date fromDate = palletVo.getStartTime();
                if (fromDate != null) {
                    expression.add(cb.greaterThanOrEqualTo(r.get("createTime").as(Date.class), fromDate));
                }
                Date toDate = palletVo.getEndTime();
                if (toDate != null) {
                    expression.add(cb.lessThanOrEqualTo(r.get("createTime").as(Date.class), toDate));
                }
                String name = palletVo.getName();
                if (StringUtils.isNotBlank(name)) {
                    expression.add(cb.like(r.get("name").as(String.class), name.trim()));
                }
                return pre;
            }
        };
    }
    
}
